package cn.schoolwow.quickserver;

import cn.schoolwow.quickserver.controller.ExceptionHandler;
import cn.schoolwow.quickserver.controller.RequestMethod;
import cn.schoolwow.quickserver.controller.ResponseBodyAdvice;
import cn.schoolwow.quickserver.controller.annotation.CrossOrigin;
import cn.schoolwow.quickserver.controller.annotation.RequestMapping;
import cn.schoolwow.quickserver.controller.annotation.RestController;
import cn.schoolwow.quickserver.domain.Client;
import cn.schoolwow.quickserver.domain.ControllerMeta;
import cn.schoolwow.quickserver.domain.ServerConfigMeta;
import cn.schoolwow.quickserver.handler.Handler;
import cn.schoolwow.quickserver.handler.HttpRequestHandler;
import cn.schoolwow.quickserver.handler.HttpResponseHandler;
import cn.schoolwow.quickserver.response.HttpStatus;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;

public class QuickServer {
    private Logger logger = LoggerFactory.getLogger(QuickServer.class);
    private ServerConfigMeta serverConfigMeta = new ServerConfigMeta();

    /**
     * 新建QuickServer实例
     */
    public static QuickServer newInstance() {
        return new QuickServer();
    }

    /**
     * 指定端口
     *
     * @param port 端口,为0表示使用系统分配端口
     */
    public QuickServer port(int port) {
        serverConfigMeta.port = port;
        return this;
    }

    /**
     * 扫描指定包
     *
     * @param packageName 包名
     */
    public QuickServer scan(String packageName) {
        serverConfigMeta.applicationContext.scan(packageName);
        return this;
    }

    /**
     * 注册指定类
     *
     * @param componentClass 容器类
     */
    public QuickServer register(Class componentClass) {
        serverConfigMeta.applicationContext.register(componentClass);
        return this;
    }

    /**
     * 指定静态路径前缀
     *
     * @param staticResourcePathPrefix 静态路径前缀
     */
    public QuickServer staticResourcePathPrefix(String staticResourcePathPrefix) {
        serverConfigMeta.staticResourcePathPrefix = staticResourcePathPrefix;
        return this;
    }

    /**
     * 指定静态路径
     *
     * @param staticResourcePath 静态路径
     */
    public QuickServer staticResourcePath(String staticResourcePath) {
        serverConfigMeta.staticResourcePathList.add(staticResourcePath);
        return this;
    }

    public void start() throws IOException {
        serverConfigMeta.applicationContext.refresh();
        getControllerMetaList();
        ServerSocket serverSocket = new ServerSocket();
        InetSocketAddress inetSocketAddress = new InetSocketAddress(serverConfigMeta.port);
        serverSocket.bind(inetSocketAddress);
        logger.info("[服务器启动]{}", inetSocketAddress);
        while (true) {
            final Socket socket = serverSocket.accept();
            serverConfigMeta.threadPoolExecutor.execute(() -> {
                Client client = new Client();
                client.socket = socket;
                client.serverConfigMeta = this.serverConfigMeta;
                Handler handler = new HttpRequestHandler();
                try {
                    while (null != handler) {
                        handler = handler.handle(client);
                    }
                } catch (Throwable e) {
                    if (e instanceof InvocationTargetException) {
                        e = ((InvocationTargetException) e).getTargetException();
                    }
                    try {
                        client.httpResponseMeta.httpStatus(HttpStatus.INTERNAL_SERVER_ERROR);
                        if (null != client.serverConfigMeta.exceptionHandler) {
                            ExceptionHandler exceptionHandler = client.serverConfigMeta.applicationContext.getBean(ExceptionHandler.class);
                            Object result = exceptionHandler.beforeBodyWrite(e, client.httpRequest, client.httpResponse, client.httpSession);
                            if (null != result) {
                                client.httpResponseMeta.contentType = "application/json";
                                client.httpResponseMeta.bodyStream.write(JSON.toJSONString(result).getBytes());
                            }
                        } else {
                            e.printStackTrace();
                        }
                        new HttpResponseHandler().handle(client);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                } finally {
                    if (null != socket && !socket.isClosed()) {
                        try {
                            socket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        }
    }

    /**
     * 提取Controller信息
     */
    private void getControllerMetaList() {
        String[] beanNames = serverConfigMeta.applicationContext.getBeanNamesForAnnotation(RestController.class);
        for (String beanName : beanNames) {
            Object instance = serverConfigMeta.applicationContext.getBean(beanName);
            String bathUrl = "";
            RequestMapping classRequestMapping = instance.getClass().getDeclaredAnnotation(RequestMapping.class);
            if (classRequestMapping != null) {
                bathUrl = classRequestMapping.value();
            }
            Method[] methods = instance.getClass().getMethods();
            for (Method method : methods) {
                RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
                if (methodRequestMapping == null) {
                    continue;
                }
                ControllerMeta controllerMeta = new ControllerMeta();
                controllerMeta.instance = instance;
                controllerMeta.mappingUrl = bathUrl + methodRequestMapping.value();
                controllerMeta.requestMethodList.addAll(Arrays.asList(methodRequestMapping.method()));
                //跨域方法添加OPTION请求支持
                if (null != method.getAnnotation(CrossOrigin.class) || null != method.getDeclaringClass().getAnnotation(CrossOrigin.class)) {
                    controllerMeta.requestMethodList.add(RequestMethod.OPTIONS);
                }
                controllerMeta.antPatternUrl = controllerMeta.mappingUrl.replaceAll("\\{\\w+\\}", "\\*");
                controllerMeta.method = method;
                serverConfigMeta.controllerMetaList.add(controllerMeta);
                logger.info("[映射路径][{},method={}] onto {}", controllerMeta.mappingUrl, controllerMeta.requestMethodList, method);
            }
        }
        try {
            serverConfigMeta.responseBodyAdvice = serverConfigMeta.applicationContext.getBean(ResponseBodyAdvice.class);
        } catch (NoSuchBeanDefinitionException e) {
        }
        try {
            serverConfigMeta.exceptionHandler = serverConfigMeta.applicationContext.getBean(ExceptionHandler.class);
        } catch (NoSuchBeanDefinitionException e) {
        }
    }
}
